package com.shop.service.impl;

import com.alibaba.fastjson.JSON;
import com.shop.fegin.ProductFeginService;
import com.shop.interceptor.CartInterceptor;
import com.shop.pojo.pms.Pms_Sku_Info;
import com.shop.service.Cart_Service;
import com.shop.utils.R;
import com.shop.utils.RedisUtil;
import com.shop.vo.Cart;
import com.shop.vo.CartItem;
import com.shop.vo.UserInfoTo;
import jdk.nashorn.internal.codegen.CompilationException;
import org.apache.commons.lang.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.checkerframework.checker.units.qual.C;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Service
public class Cart_ServiceImpl implements Cart_Service {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ProductFeginService productFeginService;
    //注入线程池
    @Autowired
    private ThreadPoolExecutor Executor;

    private final String CART_PREFIX = "cart:key_";

    /**
     * 添加商品sku以及数量到购物车中
     *
     * @param skuId
     * @param num
     * @return
     */
    @Override
    public R addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        //调用抽取出来饿方法获取用户的状态
        String redisCartKey = GetCartOps();
        //1、再添加时首先判断购物车有没有此商品 //判断当前添加的商品redis缓存中是否存在 存在数量+1 不存在则添加商品
        String reskey = (String) redisUtil.hget(redisCartKey, skuId.toString());
        if (StringUtils.isEmpty(reskey)) {
            //购物车无此商品
            //2、添加新商品到购物车
            //存储商品信息的
            CartItem cartItem = new CartItem();
            //1、调用线程池异步运行一个任务 远程查询并且封装起来
            CompletableFuture<Void> getSkuInfo = CompletableFuture.runAsync(() -> {
                //远程需要调用到当前需要添加的商品的信息  找一个根据skuID查询接口信息
                Pms_Sku_Info web_skuInfo_byID = productFeginService.get_web_SkuInfo_ByID(skuId);
                cartItem.setCheck(true);
                cartItem.setCount(num);
                cartItem.setImage(web_skuInfo_byID.getSku_default_img());
                cartItem.setName(web_skuInfo_byID.getSku_name());
                cartItem.setTitle(web_skuInfo_byID.getSku_title());
                cartItem.setSkuId(web_skuInfo_byID.getSku_id());
                cartItem.setPrice(web_skuInfo_byID.getPrice());
            }, Executor);//交给线程池调度
            //2、远程查询商品的组合信息属性
            CompletableFuture<Void> getSaleValues = CompletableFuture.runAsync(() -> {
                //调用查询商品的属性&值
                List<String> web_sku_sale_values = productFeginService.get_web_Sku_Sale_Values(skuId);
                cartItem.setSkuAttr(web_sku_sale_values);
            }, Executor);
            System.out.println("数据执行完后" + cartItem);
            //要等待这两个线程都执行完后在执行下面的
            CompletableFuture.allOf(getSkuInfo, getSaleValues).get();
            //将当前获取的信息通过JSON转成字符串
            System.out.println("数据执行完后" + cartItem.toString());
            String s = JSON.toJSONString(cartItem);
            //给redis保存信息 key Item value
            boolean hset = redisUtil.hset(redisCartKey, skuId.toString(), s);
            System.out.println("是否添加成功缓存" + hset);
            if (hset) {
                return R.success();
            }
            return R.error(500, "加购失败");

        } else {
            //如果存在该商品的话 那就只需要在数量上num+1
            //把存进去的JSON格式转回来   最后商品的数量还是要返回的我们就用一个返回
            CartItem Item = JSON.parseObject(reskey, CartItem.class);
            //修改我们原商品的数量加上再次添加商品的数量
            Item.setCount(Item.getCount() + num);
            //改回来后继续存储redis  把上面修改的值在转为JSON格式存储
            boolean hsetX = redisUtil.hset(redisCartKey, skuId.toString(), JSON.toJSONString(Item));
            System.out.println("是否修改成功缓存" + hsetX);
            if (hsetX) {
                return R.success();
            }
            return R.error(500, "加购失败");
        }
    }

    /**
     * 获取所有购物车
     *
     * @return
     */
    @Override
    public Cart getCarts() throws ExecutionException, InterruptedException {

        //返回整个购物车
        Cart cart = new Cart();
        //首先拿出我们用户登录的状态进行判断
        UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();
        //判断用户是否登录
        if (userInfoTo.getUserId() != null) {
            //1、登录
            String userId = CART_PREFIX + userInfoTo.getUserId();
            //2、需要判断临时购物车的数据是否合并  需要抽取一个获取临时购物车的方法
            //先判断临时购物车是否有数据
            List<CartItem> tempCartItems = getCartItems(CART_PREFIX + userInfoTo.getUserKey());
            if (tempCartItems != null) {
                //临时购物车有数据需要进行合并 调用添加购物车的方法 进行遍历合并
                for (CartItem tempCartItem : tempCartItems) {
                    //把遍历出来的数据进行添加合并
                    addToCart(tempCartItem.getSkuId(), tempCartItem.getCount());
                }
                //合并后清除[临时]购物车
                redisUtil.del(CART_PREFIX + userInfoTo.getUserKey());
            }
            //3、获取登录后的购物车数据 包含合并过来的临时购物车数据以及购物车数据
            List<CartItem> usercartItems = getCartItems(userId);
            //塞进购物项
            cart.setItems(usercartItems);

        } else {
            //未登录
            String userKey = CART_PREFIX + userInfoTo.getUserKey();
            //获取下面抽取出来的私有方法进行获取临时用户的所有购物项
            List<CartItem> cartItems = getCartItems(userKey);
            //将购物项放入购物车
            cart.setItems(cartItems);
        }

        System.out.println(cart.toString());
        return cart;
    }

    /**
     * 删除购物车的商品缓存信息
     *
     * @param skuId
     * @return
     */
    @Override
    public R deleteCarts(String skuId) {


        //获取当前是临时用户登录还是用户登录
        UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();
        if (userInfoTo.getUserId() != null) {
            redisUtil.hdel(CART_PREFIX + userInfoTo.getUserId(), skuId);
        } else {
            redisUtil.hdel(CART_PREFIX + userInfoTo.getUserKey(), skuId);
        }

        return R.success();
    }

    /**
     * 修改购物车商品的check选中状态
     *
     * @param skuId
     * @param check
     * @return
     */
    @Override
    public R cartUpChecked(Long skuId, Boolean check) {
        //调用下面方法获取我们的单个购物项
        CartItem cartItem = getCartItem(skuId);
        //修改购物项的状态信息
        cartItem.setCheck(check);
        //序列化进行存储
        String s = JSON.toJSONString(cartItem);
        //调用登录状态进行修改
        String s1 = GetCartOps();
        //调用修改的方法  传入ID和要修改的购物项信息
        redisUtil.hbound(s1).put(skuId.toString(), s);
        return R.success();
    }

    /**
     * 获取用户需要购买的用户项
     *
     * @return
     */
    @Override
    public List<CartItem> getUserByIdCartItems() {
        //获取当前用户登录的状态
        UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();
        if (userInfoTo == null) {
            return null;
        } else {
            String userId = CART_PREFIX + userInfoTo.getUserId();
            //找到购物车的所有购物项
            List<CartItem> cartItems = getCartItems(userId);
            //过滤一下 过滤每一项保证getCheck的属性是等于true的
            //要查询最新的商品价格 不能以购物车的价格为准
            List<CartItem> collect = cartItems.stream().filter(item -> item.getCheck())
                    .map(item->{
                        //远程调用查询最新价格 而不是缓存redis的购物车价格
                        BigDecimal zuiXinPrice = productFeginService.getZuiXinPrice(item.getSkuId());
                        // 更新为最新的商品的价格
                        item.setPrice(zuiXinPrice);
                        return item;
                    })
                    .collect(Collectors.toList());
            return collect;
        }

    }


    /**
     * 私有方法  根据skuId获取单个商品的购物项
     *
     * @param skuId
     * @return
     */
    private CartItem getCartItem(Long skuId) {
        //获取我们登录的状态是临时还是用户登录
        String s = GetCartOps();
        //根据我们的登录 状态生成的Key获取我们的商品
        String o = (String) redisUtil.hbound(s).get(skuId.toString());
        //序列化后返回
        CartItem cartItem = JSON.parseObject(o, CartItem.class);
        return cartItem;
    }


    /***
     * 抽取出来的私有方法 根据登录状态获取所有商品购物项
     * @param userKey
     * @return
     */
    private List<CartItem> getCartItems(String userKey) {
        //获取所有的临时用户的商品信息
        List<Object> values = redisUtil.hbound(userKey).values();

        if (values != null && values.size() > 0) {
            //遍历拿出值
            List<CartItem> collect = values.stream().map((obj) -> {
                //把拿到值转换成String
                String str = (String) obj;
                //把获取到的数据逆转
                CartItem cartItem = JSON.parseObject(str, CartItem.class);
                //返回逆转 数据
                return cartItem;
            }).collect(Collectors.toList()); //收集一个list

            //返回出去购物项  被调用放在购物车里
            return collect;
        }
        return null;
    }


    /**
     * 抽取出一个reids的方法进行购物车状态的查询调用
     *
     * @return
     */
    private String GetCartOps() {
        //获取我们用户的登录状态是临时用户还是以登录用户
        UserInfoTo userInfoTo = CartInterceptor.toThreadLocal.get();
        System.out.println("获取我们的状态" + userInfoTo);
        //创建一个存储的购物车内容的key
        String cartKey = "";
        //判断这个用户是否登录
        if (userInfoTo.getUserId() != null) {
            cartKey = CART_PREFIX + userInfoTo.getUserId();
        } else {
            cartKey = CART_PREFIX + userInfoTo.getUserKey();
            System.out.println("临时用户的ID:" + cartKey);
        }

        //SpringRedisTemplate.boundHashOps("存储的键名进行绑定").get~delete等方法进行绑定键名操作

        return cartKey;
    }


}
